﻿using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using DeepZoomPivotConstructor.VisualLib;
using DeepZoomPivotConstructor.VisualLib.Hotspots;
using DirectoryRender;
using GSRender;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RenderEngine;
using RenderEngineInterfaces;
using SimpleFileRendering;
using t_TestingLibrary;

namespace t_DirectoryRender
{
    /// <summary>
    /// Summary description for UnitTest1
    /// </summary>
    [TestClass]
    public class t_DirectoryInfoRender
    {
        public t_DirectoryInfoRender()
        {
        }

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        [TestInitialize]
        public void InitForTest()
        {
            t_TestingLibrary.Utilities.MyClassInit();
            t_TestingLibrary.Utilities.AddPart(new RenderControl());
            t_TestingLibrary.Utilities.AddPart(new LayoutHelper());
            t_TestingLibrary.Utilities.AddPart(new TestLogger());
        }

        [TestCleanup]
        public void ShutDownTest()
        {
            t_TestingLibrary.Utilities.MyClassDone();
        }

        [TestMethod]
        public void TestCreate()
        {
            DirectoryInfoRender r = new DirectoryInfoRender();
        }

        [TestMethod]
        public void TestDirectoryNonExistant()
        {
            DirectoryInfoRender r = new DirectoryInfoRender();
            Assert.AreEqual(RenderPluginPriority.RPPCantDoIt, r.RenderPriority(new DirectoryInfo("bogusdirinoutterspace")), "Shouldn't be able to render a non-existant directory!");
        }

        [TestMethod]
        public void TestDirectory()
        {
            DirectoryInfo d = new DirectoryInfo("TestDirectory");
            if (!d.Exists)
            {
                d.Create();
            }

            DirectoryInfoRender r = new DirectoryInfoRender();
            Assert.AreEqual(RenderPluginPriority.RPPGeneric, r.RenderPriority(d), "A real directory should be allowed to render!");
        }

        [TestMethod]
        public void TestRenderEmptyDirectory()
        {
            DirectoryInfo d = new DirectoryInfo("TestRenderEmptyDirectory");
            if (!d.Exists)
            {
                d.Create();
            }
            t_TestingLibrary.Utilities.AddPart(new RenderFileWithTitle());
            DirectoryInfoRender r = new DirectoryInfoRender();
            t_TestingLibrary.Utilities.Compose(r);
            var result = r.Render(d);
            Assert.IsNotNull(result, "Render attempt of non-existant directory should not return null");
            var visual = result.Result;
            Assert.IsNull(visual, "The result of rendering an empty directory should be null!");
        }

        [TestMethod]
        [DeploymentItem("dirPictureTest.jpg")]
        public void TestRenderDirectoryWithOneFile()
        {
            DirectoryInfo d = new DirectoryInfo("TestRenderDirectoryWithOneFile");
            if (!d.Exists)
            {
                d.Create();
            }
            CopyToDirectory(d, @"dirPictureTest.jpg");

            DirectoryInfoRender r = new DirectoryInfoRender();
            Utilities.AddPart(new JPEGFileRender());
            Utilities.AddPart(new RenderFileWithTitle());
            Utilities.Compose(r);

            var result = r.Render(d);
            Assert.IsNotNull(result, "Render attempt of non-existant directory should not return null");
            var visual = result.Result;
            Assert.IsNotNull(visual, "The result of rendering a directory should not be null!");
            Assert.AreEqual(2, visual.SGNodeList.Count, "Expected file jpeg, and title for the file");
            Assert.AreEqual(1, (from v in visual.GetHierarchicalEnumerator(v => v.IsCompositionUnit) where v.IsCompositionUnit select v).Count(), "Expected only one compositional unit!");
            Assert.AreEqual("TestRenderDirectoryWithOneFile", visual.Info.Title, "Title was not set correctly");
        }

        [TestMethod]
        [DeploymentItem("dirPictureTest.jpg")]
        public void TestRenderDirectoryWithTwoFiles()
        {
            DirectoryInfo d = new DirectoryInfo("TestRenderDirectoryWithTwoFiles");
            if (!d.Exists)
            {
                d.Create();
            }
            CopyToDirectory(d, @"dirPictureTest.jpg");
            CopyToDirectory(d, @"dirPictureTest.jpg", "file2.jpg");

            DirectoryInfoRender r = new DirectoryInfoRender();
            Utilities.AddPart(new JPEGFileRender());
            Utilities.AddPart(new RenderFileWithTitle());
            Utilities.Compose(r);

            var result = r.Render(d);
            Assert.IsNotNull(result, "Render attempt of non-existant directory should not return null");
            var visual = result.Result;
            Assert.IsNotNull(visual, "The result of rendering a directory should not be null!");
            Assert.AreEqual(4, visual.SGNodeList.Count, "Expected 2 file, 2 file titles");
            Assert.AreEqual(2, (from v in visual.GetHierarchicalEnumerator(v => v.IsCompositionUnit) where v.IsCompositionUnit select v).Count(), "Expected only one compositional unit!");
            Assert.AreEqual("TestRenderDirectoryWithTwoFiles", visual.Info.Title, "Title was not set correctly");
        }

        [TestMethod]
        [DeploymentItem("dirPictureTest.jpg")]
        public void TestRenderDirectoryWithManyFiles()
        {
            DirectoryInfo d = new DirectoryInfo("TestRenderDirectoryWithManyFiles");
            if (d.Exists)
                d.Delete();
            d.Create();
            d.Refresh();
            CopyToDirectory(d, @"dirPictureTest.jpg");
            CopyToDirectory(d, @"dirPictureTest.jpg", "file2.jpg");
            CopyToDirectory(d, @"dirPictureTest.jpg", "file3.jpg");
            CopyToDirectory(d, @"dirPictureTest.jpg", "file4.jpg");
            CopyToDirectory(d, @"dirPictureTest.jpg", "file5.jpg");
            CopyToDirectory(d, @"dirPictureTest.jpg", "file6.jpg");
            CopyToDirectory(d, @"dirPictureTest.jpg", "file7.jpg");

            DirectoryInfoRender r = new DirectoryInfoRender();
            Utilities.AddPart(new JPEGFileRender());
            Utilities.AddPart(new RenderFileWithTitle());
            Utilities.Compose(r);

            var result = r.Render(d);
            Assert.IsNotNull(result, "Render attempt of non-existant directory should not return null");
            var visual = result.Result;
            Assert.IsNotNull(visual, "The result of rendering a directory should not be null!");
            Assert.AreEqual(14, visual.SGNodeList.Count, "Expected title and one jpeg in the visual hierarchy!");
            Assert.AreEqual("TestRenderDirectoryWithManyFiles", visual.Info.Title, "Title was not set correctly");
        }

        [TestMethod]
        [DeploymentItem("dirPictureTest.jpg")]
        public void TestSubDirectoryRender()
        {
            DirectoryInfo d = new DirectoryInfo("TestSubDirectoryRender");
            if (!d.Exists)
            {
                d.Create();
            }
            DirectoryInfo dsub = new DirectoryInfo(d.FullName + "\\subone");
            if (!dsub.Exists)
            {
                dsub.Create();
            }

            CopyToDirectory(dsub, @"dirPictureTest.jpg");

            DirectoryInfoRender r = new DirectoryInfoRender();
            Utilities.AddPart(new JPEGFileRender());
            Utilities.AddPart(new RenderFileWithTitle());
            Utilities.Compose(r);

            var result = r.Render(d);
            Assert.IsNotNull(result, "Render attempt of non-existant directory should not return null");
            var visual = result.Result;
            Assert.IsNotNull(visual, "The result of rendering a directory should not be null!");
            Assert.AreEqual(3, visual.SGNodeList.Count, "Expected 1 file 1 title and 1 dir title");
            Assert.AreEqual("TestSubDirectoryRender", visual.Info.Title, "Title was not set correctly");
            foreach (var v in visual.GetHierarchicalEnumerator(v => v.IsCompositionUnit))
            {
                if (v.IsCompositionUnit)
                {
                    Assert.IsNotNull(v.Info, "Info of everything that might be composed can't be null!");
                    Assert.IsNotNull(v.Info.Title, "Title must not be null of things being rendered as compositional units!");
                }
            }
        }

        [TestMethod]
        [DeploymentItem("dirPictureTest.jpg")]
        public void TestBadFile()
        {
            /// Make sure we can deal with a file we don't know how to render

            DirectoryInfo d = new DirectoryInfo("TestBadFile");
            if (!d.Exists)
            {
                d.Create();
            }
            CopyToDirectory(d, @"dirPictureTest.jpg");

            DirectoryInfoRender r = new DirectoryInfoRender();
            Utilities.AddPart(new RenderFileWithTitle());
            Utilities.Compose(r);

            var result = r.Render(d);
            Assert.IsNotNull(result, "Render attempt of non-existant directory should not return null");
            var visual = result.Result;
            Assert.IsNull(visual, "The result of rendering an empty directory should be null (or dir with no files)!");
        }

        [TestMethod]
        [DeploymentItem("dirPictureTest.jpg")]
        public void TestBadDir()
        {
            /// make sure a subdir that contains a no-good file only renders properly.

            DirectoryInfo d = new DirectoryInfo("TestBadDir");
            if (!d.Exists)
            {
                d.Create();
            }
            DirectoryInfo dsub = new DirectoryInfo(d.FullName + "\\subone");
            if (!dsub.Exists)
            {
                dsub.Create();
            }

            CopyToDirectory(dsub, @"dirPictureTest.jpg");

            DirectoryInfoRender r = new DirectoryInfoRender();
            Utilities.AddPart(new RenderFileWithTitle());
            Utilities.Compose(r);

            var result = r.Render(d);
            Assert.IsNotNull(result, "Render attempt of non-existant directory should not return null");
            var visual = result.Result;
            Assert.IsNull(visual, "The result of rendering an empty directory should be null!");
        }

        [TestMethod]
        [DeploymentItem("regularPDFfile.pdf")]
        public void TestTitleSize()
        {
            DirectoryInfo d = new DirectoryInfo("TestTitleSize");
            if (!d.Exists)
            {
                d.Create();
            }
            CopyToDirectory(d, @"regularPDFfile.pdf");

            DirectoryInfoRender r = new DirectoryInfoRender();
            Utilities.AddPart(new JPEGFileRender());
            Utilities.AddPart(new RenderUsingGS());
            Utilities.AddPart(new RenderFileWithTitle());
            Utilities.Compose(r);

            var result = r.Render(d);
            Assert.IsNotNull(result, "Render attempt of non-existant directory should not return null");
            var visual = result.Result;
            Assert.AreEqual(10.4, visual.Width, "The width of the talk should be 10.4 or so (10.0 + 0.2 for the boarders)...");
            Assert.IsTrue(visual.Height < 20.0, "The height is too high!");
        }

        /// <summary>
        /// So we can do rendering too. Make sure to call compose first!
        /// </summary>
        [Import]
        private IRenderEngine MyRender { get; set; }

        [TestMethod]
        [DeploymentItem("dirPictureTest.jpg")]
        public void TestMetadata()
        {
            DirectoryInfo d = new DirectoryInfo("TestMetadatat_DirectoryInfoRenderTests");
            if (d.Exists)
            {
                d.Delete();
            }
            d.Create();
            CopyToDirectory(d, @"dirPictureTest.jpg");

            Utilities.AddPart(new JPEGFileRender());
            Utilities.AddPart(new DirectoryInfoRender());
            Utilities.AddPart(new RenderFileWithTitle());
            Utilities.Compose(this);


            var visual = MyRender.Render(d);
            Assert.IsNotNull(visual, "Render attempt of non-existant directory should not return null");
            Assert.AreEqual(2, visual.SGNodeList.Count, "Expected title and one jpeg in the visual hierarchy!");
            Assert.AreEqual("TestMetadatat_DirectoryInfoRenderTests", visual.Info.Title, "Title was not set correctly");
        }

        /// <summary>
        /// Make a copy of a source file to a destination file.
        /// </summary>
        /// <param name="d"></param>
        /// <param name="p"></param>
        private void CopyToDirectory(DirectoryInfo d, string p)
        {
            FileInfo f = new FileInfo(p);
            CopyToDirectory(d, p, f.Name);
        }

        /// <summary>
        /// Copy a file over to a (possibly) new name.
        /// </summary>
        /// <param name="d"></param>
        /// <param name="p"></param>
        /// <param name="p_3"></param>
        private void CopyToDirectory(DirectoryInfo desDirectory, string sourceFile, string finalName)
        {
            FileInfo s = new FileInfo(sourceFile);
            Assert.IsTrue(s.Exists, "Source file does not exist!");
            FileInfo finalFile = new FileInfo(desDirectory.FullName + "\\" + finalName);
            s.CopyTo(finalFile.FullName);
        }

        [Export(typeof(IRenderItem))]
        [RenderInfo(typeof(FileInfo))]
        class RenderJPEGCounter : IRenderItem
        {
            public int Count { get; set; }

            public RenderJPEGCounter()
            {
                Count = 0;
            }

            public Task<Visual> Render(object what)
            {
                return Task<Visual>.Factory.StartNew(() =>
                    {
                        lock (this)
                        {
                            Count = Count + 1;
                        }
                        Visual b = new VisualBitmapImage(1.0, 1.0, 100, Color.Black);
                        b.Info = new Visual.VisualInfo();
                        b.Info.Title = "hi";
                        return b;
                    });
            }

            public RenderPluginPriority RenderPriority(object what)
            {
                FileInfo f = what as FileInfo;
                if (f.Extension == ".jpg")
                {
                    return RenderPluginPriority.RPPGeneric;
                }
                return RenderPluginPriority.RPPCantDoIt;
            }
        }

        [TestMethod]
        public void TestTwoFiles()
        {
            ///
            /// Make sure the right number of things are called the right number of
            /// times.
            ///

            DirectoryInfo info = new DirectoryInfo(".\\TestTwoFiles");
            info.Create();
            int nDir = 0;
            int nFiles = 2;
            int deep = 1;

            MakeDeepDirectoryStructure(info, nDir, nFiles, deep);

            RenderJPEGCounter counter = new RenderJPEGCounter();
            Utilities.AddPart(counter);
            Utilities.AddPart(new DirectoryInfoRender());
            Utilities.AddPart(new RenderFileWithTitle());
            Utilities.Compose(this);

            Visual v = MyRender.Render(info);
            Assert.AreEqual(2, counter.Count, "JPEG rendering dude was called wrong number of times");
        }

        [TestMethod]
        public void TestComplexDirectoryStructure()
        {
            ///
            /// A directory structure that is pretty complex
            /// 

            DirectoryInfo info = new DirectoryInfo(".\\TestComplexDirectoryStructure");
            info.Create();
            int nDir = 5;
            int nFiles = 5;
            int deep = 2;

            MakeDeepDirectoryStructure(info, nDir, nFiles, deep);

            ///
            /// Now do the rendering
            /// 

            Utilities.AddPart(new JPEGFileRender());
            Utilities.AddPart(new DirectoryInfoRender());
            Utilities.AddPart(new RenderFileWithTitle());
            Utilities.Compose(this);

            Visual vis = null;
            Task.Factory.StartNew(() =>
                {
                    vis = MyRender.Render(info);
                }).Wait();
            Assert.IsNotNull(vis, "Render attempt of non-existant directory should not return null");
        }

        /// <summary>
        /// Create a "complex" directory structure.
        /// </summary>
        /// <param name="info"></param>
        /// <param name="nDir"></param>
        /// <param name="nFiles"></param>
        /// <param name="deep"></param>
        private void MakeDeepDirectoryStructure(DirectoryInfo info, int nDir, int nFiles, int deep)
        {
            if (deep == 0)
            {
                return;
            }

            for (int i = 0; i < nDir; i++)
            {
                DirectoryInfo newDir = new DirectoryInfo(info.FullName + "\\dir_" + i.ToString());
                newDir.Create();
                MakeDeepDirectoryStructure(newDir, nDir, nFiles, deep - 1);
            }
            for (int i = 0; i < nFiles; i++)
            {
                CopyToDirectory(info, @"dirPictureTest.jpg", "file_" + i.ToString() + ".jpg");
            }
        }

        [TestMethod]
        [DeploymentItem("dirPictureTest.jpg")]
        public void TestEmptySubDir()
        {
            /// Make sure a subdirectory that is empty doesn't cause a fail.

            ///
            /// A directory structure that is pretty complex
            /// 

            DirectoryInfo info = new DirectoryInfo(".\\TestEmptySubDir");
            info.Create();
            DirectoryInfo subInfo = new DirectoryInfo(info.FullName + "\\bogusempty");
            subInfo.Create();
            DirectoryInfo subsubInfo = new DirectoryInfo(subInfo.FullName + "\\really");
            subsubInfo.Create();

            CopyToDirectory(info, @"dirPictureTest.jpg");

            ///
            /// Now do the rendering
            /// 

            Utilities.AddPart(new JPEGFileRender());
            Utilities.AddPart(new DirectoryInfoRender());
            Utilities.AddPart(new RenderFileWithTitle());
            Utilities.Compose(this);

            var vis = MyRender.Render(info);

            Assert.IsNotNull(vis, "Render attempt of non-existant directory should not return null");
        }

        [TestMethod]
        [DeploymentItem("dirPictureTest.jpg")]
        public void TestForHSOneDir()
        {
            DirectoryInfo d = new DirectoryInfo("TestForHS");
            if (!d.Exists)
            {
                d.Create();
            }
            CopyToDirectory(d, @"dirPictureTest.jpg");

            DirectoryInfoRender r = new DirectoryInfoRender();
            Utilities.AddPart(new JPEGFileRender());
            Utilities.AddPart(new RenderFileWithTitle());
            Utilities.Compose(r);

            var result = r.Render(d);
            var visual = result.Result;

            var allHS = from v in visual.GetHierarchicalEnumerator((o) => false)
                        where v.GetType() == typeof(VisualHotSpot)
                        select v;

            Assert.AreEqual(1, allHS.Count(), "Should have found only a single hot spot!");
            VisualHotSpot lnk = allHS.First() as VisualHotSpot;
            TestActions(lnk, new string[] { "uri" });
        }

        /// <summary>
        /// Test the set of actions and amke sure they match what we expect.
        /// </summary>
        /// <param name="lnk"></param>
        /// <param name="p"></param>
        private void TestActions(VisualHotSpot lnk, string[] actions)
        {
            List<string> l = new List<string>(actions);

            lnk.GetAllActoinData((action) =>
                {
                    Assert.IsTrue(l.Contains(action.LinkType));
                    l.Remove(action.LinkType);
                });
            Assert.AreEqual(0, l.Count, "Expected all the link tpyes to be gone!");
        }

        [TestMethod]
        [DeploymentItem("dirPictureTest.jpg")]
        public void TestForHSSubDir()
        {
            DirectoryInfo d = new DirectoryInfo("TestForHSSubDir");
            if (!d.Exists)
            {
                d.Create();
            }
            DirectoryInfo dsub = new DirectoryInfo(d.FullName + "\\subone");
            if (!dsub.Exists)
            {
                dsub.Create();
            }

            CopyToDirectory(dsub, @"dirPictureTest.jpg");

            DirectoryInfoRender r = new DirectoryInfoRender();
            Utilities.AddPart(new JPEGFileRender());
            Utilities.AddPart(new RenderFileWithTitle());
            Utilities.Compose(r);

            var visual = r.Render(d).Result;
            var allHS = from v in visual.GetHierarchicalEnumerator((o) => false)
                        where v.GetType() == typeof(VisualHotSpot)
                        select v;

            Assert.AreEqual(2, allHS.Count(), "Should have found only two hot spots!");

        }
    }
}
